/**
* @author zqp
* @version2017年12月23日 
* @description 
*/
package com.zqp.security.core.validator.code.impl;

import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import com.zqp.security.core.validator.code.ValidateCode;
import com.zqp.security.core.validator.code.ValidateCodeException;
import com.zqp.security.core.validator.code.ValidateCodeGenerator;
import com.zqp.security.core.validator.code.ValidateCodeProcessor;
import com.zqp.security.core.validator.code.ValidateCodeType;

/**
 * @author zqp
 * @param <C>
 * @version2017年12月23日
 * @description
 */
public abstract class AbstractValidateCodeProcessor<C extends ValidateCode> implements ValidateCodeProcessor {

	/***
	 * 操作session
	 */
	private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();

	/**
	 * 收集系统中所有的验证码实现 以他们的key 为键. {@link ValidateCodeGenerator}} 接口的实现
	 */
	@Autowired
	private Map<String, ValidateCodeGenerator> validateCodeGenerators;

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.zqp.security.core.validator.code.ValidateCodeProcessor#createCode(org.
	 * springframework.web.context.request.ServletWebRequest)
	 */
	@Override
	public void createCode(ServletWebRequest request) throws Exception {
		C validateCode = generate(request);
		save(request, validateCode);
		send(request, validateCode);
	}

	/**
	 * 生成校验码
	 * 
	 * @param request
	 * @return
	 * @see
	 */
	private C generate(ServletWebRequest request) {
		String type = getProcessorType(request);

		ValidateCodeGenerator validateCodeGenerator = validateCodeGenerators.get(type + "CodeGenerator");

		return (C) validateCodeGenerator.createCode(request);
	}

	/**
	 * 根据URL 获取校验吗的类型
	 * 
	 * @param request
	 * @return
	 * @see
	 */
	private String getProcessorType(ServletWebRequest request) {
		return StringUtils.substringAfter(request.getRequest().getRequestURI(), "/code/");

	}

	/**
	 * 保存验证码
	 * -->  不保存图片只保存验证吗
	 * @param request
	 * @param validataCode
	 * @see
	 */
	private void save(ServletWebRequest request, C validateCode) {
		ValidateCode code = new ValidateCode(validateCode.getCode(),validateCode.getExpireTime());
		sessionStrategy.setAttribute(request, getSessionKey(request), code);
      
	} 

	/**
	 * 构建验证码放入session时的key
	 * 
	 * @param request
	 * @return
	 */
	public String getSessionKey(ServletWebRequest request) {
		return SESSION_KEY_PREFIX + getValidateCodeType(request).toString().toUpperCase();

	}

	/**
	 * 根据请求的url获取校验码的类型
	 * 
	 * @param request
	 * @return
	 * @return
	 */
	private ValidateCodeType getValidateCodeType(ServletWebRequest request) {
		System.out.println("获取类型.." + getClass().getSimpleName());
		String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor");
		return ValidateCodeType.valueOf(type.toUpperCase());

	}

	/**
	 * 发送校验码，由子类实现
	 * 
	 * @param request
	 * @param validateCode
	 * @throws Exception
	 */
	protected abstract void send(ServletWebRequest request, C validateCode) throws Exception;

	@SuppressWarnings("unchecked")
	@Override
	public void validate(ServletWebRequest request) {

		ValidateCodeType processorType = getValidateCodeType(request);
		String sessionKey = getSessionKey(request);

		C codeInSession = (C) sessionStrategy.getAttribute(request, sessionKey);

		String codeInRequest;
		try {
			codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(),
					processorType.getParamNameOnValidate());
		} catch (ServletRequestBindingException e) {
			throw new ValidateCodeException("获取验证码的值失败");
		}

		if (StringUtils.isBlank(codeInRequest)) {
			throw new ValidateCodeException(processorType + "验证码的值不能为空");
		}

		if (codeInSession == null) {
			throw new ValidateCodeException(processorType + "验证码不存在");
		}

		if (codeInSession.isExpried()) {
			sessionStrategy.removeAttribute(request, sessionKey);
			throw new ValidateCodeException(processorType + "验证码已过期");
		}

		if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
			throw new ValidateCodeException(processorType + "验证码不匹配");
		}

		sessionStrategy.removeAttribute(request, sessionKey);
	}
}
